package cn.sc.summer.constant.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import cn.sc.summer.constant.regex.RegexConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import javax.script.Compilable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 类名：javaScript执行工具类
 *
 * @author a-xin
 * @date 2024/4/23 17:55
 */
@Slf4j
@Component
public class JavaScriptUtil {

    /**
     * js引擎名称
     */
    private static final String ENGINE_NAME = "javascript";

    @Bean
    public ScriptEngine createScriptEngine() {
        log.info("==> Start initialize the JS engine...");
        return new ScriptEngineManager().getEngineByName(ENGINE_NAME);
    }

    /**
     * 执行指定JS代码
     *
     * @param function js代码
     * @param params   参数信息
     * @return js执行返回结果信息
     */
    public static Object runJavaScriptCode(@NonNull String function, Map<String, Object> params) {

        String functionName = getJSFunctionName(function);
        List<String> functionParameters = getJSFunctionParameters(function);

        ScriptEngine engine = SpringUtil.getBean(ScriptEngine.class);
        if (engine instanceof Compilable) {
            try {
                engine.eval(function);
                StringBuilder stringBuilder = new StringBuilder(functionName)
                        .append("(");
                AssertUtil.functionIfPre(functionParameters, CollUtil::isNotEmpty, () -> {
                    List<String> collect = functionParameters.stream()
                            .map(item -> {
                                if (!params.containsKey(item)) {
                                    throw new RuntimeException("js代码参数：[" + item + "] 不存在");
                                }
                                return params.get(item).toString();
                            })
                            .collect(Collectors.toList());
                    stringBuilder.append(String.join(",", collect));
                });
                stringBuilder.append(")");
                return engine.eval(stringBuilder.toString());
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e.getMessage());
            }
        }
        return null;
    }

    /**
     * 执行指定JS代码并将结果转换成需要的类型
     *
     * @param function js代码
     * @param params   参数信息
     * @param clsType  结果类型，可带泛型
     * @param <T>      结果转换类型
     * @return js执行返回结果信息
     */
    public static <T> T runJavaScriptCode(@NonNull String function, Map<String, Object> params, TypeReference<T> clsType) {
        String jsonString = ConvertUtil.toJsonString(runJavaScriptCode(function, params));
        if (StrUtil.isNotBlank(jsonString)) {
            return ConvertUtil.toObject(jsonString, clsType);
        }
        return null;
    }

    /**
     * 执行指定JS代码并将结果转换Map
     *
     * @param function   js代码
     * @param params     参数信息
     * @param resultName 结果名称
     * @return js执行返回结果信息
     */
    public static Map<String, Object> runJavaScriptCodeToMap(@NonNull String function, Map<String, Object> params, @NonNull String resultName) {
        String jsonString = ConvertUtil.toJsonString(runJavaScriptCode(function, params));
        Map<String, Object> result = new HashMap<>();
        result.put(resultName, StrUtil.isNotBlank(jsonString) ? jsonString : "");
        return result;
    }

    /**
     * js方法名获取，如有多个方法则获取第一个
     *
     * @param jsCode js代码
     * @return 方法名
     */
    private static String getJSFunctionName(@NonNull String jsCode) {
        Pattern pattern = Pattern.compile(RegexConstant.JS_FUNCTION_NAME);
        Matcher matcher = pattern.matcher(jsCode);
        if (matcher.find()) {
            return matcher.group(1).replaceAll(" ", "");
        }
        throw new RuntimeException("javaScript方法名异常!");
    }

    /**
     * js方法参数获取，如有多个方法则获取第一个方法的参数信息
     *
     * @param jsCode js代码
     * @return 参数列表，有序
     */
    private static List<String> getJSFunctionParameters(@NonNull String jsCode) {
        Pattern pattern = Pattern.compile(RegexConstant.JS_FUNCTION_PARAM);
        Matcher matcher = pattern.matcher(jsCode);
        if (matcher.find()) {
            String parametersStr = matcher.group(1).replaceAll(" ", "");
            return Arrays.stream(parametersStr.split(",")).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

}
